/**
 * Demonstrate the use of signals with
 * synchronization
 *
 * Author: Renê S. Pinto
 * Date: Apr / 2009
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "linkedl.h"

extern char **environ;

void install_handler();
void handler(int signum);
void print_list();


static linked_list *plist; /* Proccess list */

sem_t *sem; /* Semaphore */


int main(int argc, char **argv)
{
	char *path;
	char str_path[4096];
	size_t spos, pos, len, msize;
	//int status;
	pid_t child;

	if(argc < 2) {
		printf("Use: %s <command>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	sem = sem_open("SEM_SH", O_CREAT, O_RDWR, 0);
	if(sem == SEM_FAILED) {
		perror("Error on creating semaphore!\n");
		exit(EXIT_FAILURE);
	}

	llist_create(&plist);
	install_handler();

	// Execute command
	if( !(child = fork()) ) {

		// Here semaphore will lock until father unlock it
		sem_wait(sem);

		// Get path dir
		path = getenv("PATH");
		if(path != NULL) {
			// Parse PATH
			spos  = pos = 0;
			len   = strlen(path);
			while(spos < len) {
				while(path[pos] != ':' && path[pos] != '\0')
					pos++;

				if(pos >= msize) {
					exit(EXIT_FAILURE);
				} else {
					strncpy(str_path, &path[spos], (pos - spos));
					str_path[(pos - spos)] = '\0';
					strcat(str_path, "/");
					strcat(str_path, argv[1]);
				}
				spos = ++pos;
				execve(str_path, NULL, environ);
			}
			// If the program is here, all directories tryed failed
			perror("\nCommand not found.\n");
			exit(EXIT_FAILURE);
		} else {
			execve(argv[1], NULL, environ);
			perror("\nCommand not found.\n");
			exit(EXIT_FAILURE);
		}
	} else {
		// Father
		//waitpid(child, &status, 0);
		llist_add(&plist, "PROC1");

		// Unlock semaphore
		sem_post(sem);

		while(1);
	}

	return(EXIT_SUCCESS);
}


/* Install signal handler */
void install_handler()
{
	struct sigaction act;

	act.sa_handler  = handler;
	act.sa_restorer = NULL;
	sigaction(SIGCHLD, &act, NULL);
	sigaction(SIGTSTP, &act, NULL);
	sigaction(SIGINT,  &act, NULL);
}


/* Handle signals */
void handler(int signum)
{
	switch(signum) {
		case SIGCHLD:
			llist_remove(&plist, 0);
			sem_close(sem);
			printf("My sun is gone!\n");
			fflush(stdout);
			break;

		case SIGTSTP:
			print_list();
			break;

		case SIGINT:
			printf("Bye Bye!\n");
			fflush(stdout);
			llist_destroy(&plist);
			sem_unlink("SEM_SH");
			exit(EXIT_SUCCESS);
	}
}


/* Print proccess list */
void print_list()
{
	linked_list *tmp = plist;

	if(!tmp) {
		printf("PLIST are empty!\n");
		return;
	}

	while(tmp != NULL) {
		printf("> %s\n", (char *)tmp->element);
		tmp = tmp->next;
	}
}
